在雲端環境中,安全一直是所有 SRE 至關重要的議題,每個雲服務都有不同的最佳權限控管方式,從 AWS 轉移至GCP 後嘗試了在 GKE 中使用各種方式,例如:Instance Service Account, SA credential Json Token 踩了很多坑後,發現Workload Identity 才是 GKE 內權限控管的最佳實踐方式。本文將深入探討 GKE 的 Workload Identity 功能,說明其運作原理以及如何設定。
為了解決 GKE 內的服務連接到其他 GCP 服務,Google 為 GKE 用戶推出 Workload Identity 功能,透過建立 GKE Service Account 和 Cloud IAM Service Account 之間的關聯,用戶能夠定義Workload Identity,不需要管理 GKE Secret 機密資訊或是 IAM Service Account Token,GKE 應用程式就能自動地存取其他雲端服務。
GKE Service Account 與 Cloud IAM Service Account 之間的關聯是由 Identity Namespace 定義,當用戶在 GKE 叢集啟用 Workload Identity 之後,專案會便會收到 Identity Namespace,共用 GKE Service Account 名稱、GKE Namespace 以及 Identity Namespace 的應用程式,將獲得存取 Cloud IAM Service Account 的權限。
以下圖為例,左側 GKE Cluster 中有兩個 Namespace,當中各有自己的 Service Account,各自綁定到GKE 外部不同的 GCP IAM 的 Service Account,然後 GCP IAM 中的 Service Account 有各自訪問不同 GCP Pub/Sub 服務的權限,如此一來 GKE 內的 Pod 即可以 Service Account 來訪問 GKE 外的其他服務。
先到 GKE Custer 設定打開 GKE Workload Identity
接下來讓我們使用 Terraform 來建立 Workload Identity , GKE Service Account ,IAM Policy Binding,當然也可以使用指令創建,這邊就以 Terraform 來作為示範
# main.tf
module "service_accounts_workload_identity" {
source = "terraform-google-modules/kubernetes-engine/google//modules/workload-identity"
project_id = var.project
cluster_name = var.k8s_cluster
location = var.k8s_location
automount_service_account_token = true
for_each = var.service_accounts
name = each.key
gcp_sa_name = each.value.gcp_service_account
k8s_sa_name = each.value.k8s_service_account
namespace = each.value.k8s_service_account_namespace
use_existing_gcp_sa = each.value.use_existing_gcp_sa
use_existing_k8s_sa = each.value.use_existing_k8s_sa
roles = each.value.roles
additional_projects = each.value.additional_projects
}
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "5.14.0"
}
}
required_version = ">= 0.14"
}
provider "google" {
project = var.project
region = var.region
zone = var.zone
}
provider "google-beta" {
project = var.project
}
provider "kubernetes" {
host = "https://${data.google_container_cluster.k8s_cluster.endpoint}"
token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(
data.google_container_cluster.k8s_cluster.master_auth[0].cluster_ca_certificate,
)
}
# variable.tf
data "google_container_cluster" "k8s_cluster" {
name = var.k8s_cluster
location = var.k8s_location
}
data "google_client_config" "default" {}
# 填入GKE專案名
variable "project" {
description = "GCP project ID"
type = string
default = "ithome-202409-demo-2"
}
# 填入地區
variable "region" {
description = "Name of the GCP region"
type = string
default = "us-central1"
}
variable "zone" {
description = "Name of the GCP region"
type = string
default = "us-central1-a"
}
# 填入GKE名稱
variable "k8s_cluster" {
default = "demo2-cluster"
}
# 填入GKE地區
variable "k8s_location" {
default = "us-central1"
}
## Service Account
variable "service_accounts" {
default = {
cert-manager = {
gcp_service_account = "cert-manager"
k8s_service_account = "cert-manager"
k8s_service_account_namespace = "cert-manager"
use_existing_gcp_sa = false
use_existing_k8s_sa = false
roles = [
],
# 填入管理 Cloud DNS 所在的專案
additional_projects = {
"ithome-202409-demo" = [
"roles/dns.admin"
]
}
}
}
}
在 ithome-202409-demo-2 專案下創建 cert-manager@ithome-202409-demo-2.iam.gserviceaccount.com
服務帳戶,此帳戶具有 ithome-202409-demo 專案的 DNS 管理員權限,IAM Policy Binding 到 cert-manager 命名空間下的 cert-manager 服務帳戶
以下表來說明
GCP 服務帳戶 | 角色 | GKE 服務帳戶 | GKE Namespace |
---|---|---|---|
cert-manager@ithome-202409-demo-2.iam.gserviceaccount.com |
ithome-202409-demo專案下的 DNS Administrator | cert-manager | cert-manager |
成功後的 GCP IAM
會發現 GKE 內的 Service Account 會多一個 Annotation
metadata:
annotations:
iam.gke.io/gcp-service-account: cert-manager@ithome-202409-demo-2.iam.gserviceaccount.com
Workload Identity 是 GCP GKE 中特有的權限管理方式,解決了自己困擾了很久的 GKE Pod IAM 問題,無需在 GKE 內加入敏感資訊,例如:Token, Secret Key等,既可以防止金鑰洩漏,且方便進行服務的權限稽核,再配合上 Terraform 自動化使 SRE 的日常權限控管更加方便。